#include <semaphore.h>
#include <string.h>
#include <sys/mman.h>
#include <sys/shm.h>
#include <unistd.h>
#include <wait.h>

#include <iostream>

using namespace std;

int main(int argc, char const *argv[]) {
  sem_t *semid = nullptr;
  // 父子进程中创建信号量需要使用mmap，因为fork时会将上下文拷贝一份，两个semid指向不同的地址
  semid =
      (sem_t *)mmap(NULL,                   /* 由系统分配地址 */
                    sizeof(sem_t),          /* 申请的地址大小 */
                    PROT_READ | PROT_WRITE, /* 映射区域的保护方式（读写权限） */
                    MAP_SHARED | MAP_ANON, /* 进程共享，映射区与文件不关联 */
                    -1, /* 文件描述符，匿名映射传入-1 */
                    0); /* 偏移量 */
  // 无名信号量
  // 参数：待初始化信号量，线程0/1进程间共享，信号量初始值
  sem_init(semid, 1, 0);

  int shmid = 0;
  // 创建共享内存
  // 参数：Key，创建的大小，所需的权限
  key_t key = ftok("./t", 666);
  shmid = shmget(key, 1024, IPC_CREAT | 0666);
  if (shmid < 0) {
    perror("shmget");
    exit(1);
  }

  pid_t pid = fork();
  if (pid < 0) {
    perror("fork");
  } else if (pid == 0) {
    // 挂接共享内存
    // 参数：共享内存ID，连接到共享内存的地址位，连接的权限
    char *shm_addrs = static_cast<char *>(shmat(shmid, NULL, 0));
    for (int i = 0; i < 10; ++i) {
      shm_addrs[i] = 'A' + i;
      cout << "PROCESS..." << endl;
    }
    shmdt(shm_addrs);
    if (sem_post(semid) != 0) {
      perror("sem_post");
    }
    exit(0);
  }

  if (sem_wait(semid) != 0) {
    perror("sem_wait");
  }
  char *shm_addr = static_cast<char *>(shmat(shmid, NULL, 0));
  for (int i = 0; i < 10; ++i) cout << i + 1 << " READ " << shm_addr << endl;
  shmdt(shm_addr);

  wait(NULL);
  // 去除关联共享内存
  // 销毁共享内存
  if (shmctl(shmid, IPC_RMID, NULL) < 0) {
    perror("shmctl");
  }
  // 销毁无名信号量
  sem_destroy(semid);
  munmap(semid, sizeof(sem_t));
  return 0;
}
